CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/next/pages/token/[id].tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
Visit
8
9
https://cocalc.com/token/vZmCKcIMha2nKyFQ0rgK
10
11
to carry out the action associated with the token vZmCKcIMha2nKyFQ0rgK.
12
*/
13
14
import Footer from "components/landing/footer";
15
import Head from "components/landing/head";
16
import Header from "components/landing/header";
17
import { Customize, CustomizeType } from "lib/customize";
18
import withCustomize from "lib/with-customize";
19
import useAPI from "lib/hooks/api";
20
import { Alert, Button, Card, Divider, Layout, Space, Spin } from "antd";
21
import { useRouter } from "next/router";
22
import type { Description } from "@cocalc/util/db-schema/token-actions";
23
import { capitalize } from "@cocalc/util/misc";
24
import { useState } from "react";
25
import { getTokenDescription } from "@cocalc/server/token-actions/handle";
26
import Markdown from "@cocalc/frontend/editors/slate/static-markdown";
27
import { Icon, IconName } from "@cocalc/frontend/components/icon";
28
import getAccountId from "lib/account/get-account";
29
import InPlaceSignInOrUp from "components/auth/in-place-sign-in-or-up";
30
31
const STYLE = { margin: "30px auto", maxWidth: "600px", fontSize: "14pt" };
32
33
export async function getServerSideProps(context) {
34
const { id: token_id } = context.params;
35
const account_id = await getAccountId(context.req);
36
let description;
37
try {
38
description = await getTokenDescription(token_id, account_id);
39
} catch (error) {
40
description = {
41
type: "error",
42
title: "Error",
43
details: `${error}`,
44
cancelText: "",
45
okText: "OK",
46
};
47
}
48
return await withCustomize({ context, props: { token_id, description } });
49
}
50
51
interface Props {
52
customize: CustomizeType;
53
token_id: string;
54
description: Description & {
55
title?: string;
56
details?: string;
57
okText?: string;
58
cancelText?: string;
59
icon?: IconName;
60
signIn?: boolean;
61
};
62
}
63
64
export default function TokenActions({
65
customize,
66
description,
67
token_id,
68
}: Props) {
69
const router = useRouter();
70
const [doAction, setDoAction] = useState<boolean>(false);
71
const [loading, setLoading] = useState<boolean>(false);
72
const title = getTitle(description);
73
74
return (
75
<Customize value={customize}>
76
<Head title={title} />
77
<Layout>
78
<Header />
79
{!!description.signIn && (
80
<div style={{ marginTop: "30px" }}>
81
<InPlaceSignInOrUp
82
title={"Please create an account (which is very easy) or sign in"}
83
/>
84
</div>
85
)}
86
<Dialog
87
disabled={doAction || !!description.signIn}
88
loading={loading}
89
title={title}
90
details={description.details}
91
okText={description.okText}
92
cancelText={description.cancelText}
93
icon={description.icon}
94
onConfirm={() => {
95
setDoAction(true);
96
}}
97
onCancel={() => {
98
setLoading(true);
99
router.push("/");
100
}}
101
/>
102
{!description.signIn && doAction && <HandleToken token={token_id} />}
103
<Footer />
104
</Layout>
105
</Customize>
106
);
107
}
108
109
function Dialog({
110
disabled,
111
title,
112
details,
113
okText,
114
cancelText,
115
icon,
116
onConfirm,
117
onCancel,
118
loading,
119
}) {
120
return (
121
<Card
122
style={{
123
margin: "30px auto",
124
minWidth: "400px",
125
maxWidth: "min(700px,100%)",
126
}}
127
title={
128
<Space>
129
{icon && <Icon name={icon} />}
130
<Markdown value={title} style={{ marginBottom: "-1em" }} />
131
</Space>
132
}
133
>
134
{details && <Markdown value={details} />}
135
<Divider />
136
<div style={{ float: "right" }}>
137
<Space style={{ marginTop: "8px" }}>
138
{loading && <Spin />}
139
{cancelText != "" && (
140
<Button onClick={onCancel} disabled={disabled || loading}>
141
{cancelText ?? "Cancel"}
142
</Button>
143
)}
144
{okText != "" && (
145
<Button
146
onClick={onConfirm}
147
disabled={disabled || loading}
148
type="primary"
149
>
150
{okText ?? "Confirm"}
151
</Button>
152
)}
153
</Space>
154
</div>
155
</Card>
156
);
157
}
158
159
function HandleToken({ token }) {
160
const { calling, result, error } = useAPI("token-action", { token });
161
162
return (
163
<div>
164
{calling && (
165
<div style={{ ...STYLE, textAlign: "center" }}>
166
<Spin />
167
</div>
168
)}
169
{error && <Alert showIcon style={STYLE} type="error" message={error} />}
170
{!calling && result != null && !error && (
171
<RenderResult data={result.data} />
172
)}
173
</div>
174
);
175
}
176
177
function RenderResult({ data }: { data: any }) {
178
if (data?.type == "create-credit") {
179
const { session, instructions } = data;
180
return (
181
<Alert
182
showIcon
183
style={STYLE}
184
type="warning"
185
message="Make a Payment"
186
description={<a href={session.url}>{instructions}</a>}
187
/>
188
);
189
} else {
190
return (
191
<Alert
192
showIcon
193
style={STYLE}
194
type="success"
195
message="Success!"
196
description={data?.text ? <Markdown value={data?.text} /> : undefined}
197
/>
198
);
199
}
200
}
201
202
function getTitle({ title, type }: Description & { title?: string }) {
203
if (title) {
204
return title;
205
}
206
switch (type) {
207
case "make-payment":
208
return "Make a Payment";
209
case "disable-daily-statements":
210
return "Disable Daily Statements";
211
default:
212
if (typeof type == "string" && type) {
213
return capitalize(type.replace(/-/g, " "));
214
}
215
return "Token Action";
216
}
217
}
218
219